﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.Serialization;
using Microsoft.Practices.EnterpriseLibrary.Validation;


namespace Httlgd.DataCenter.Services.Core.Common.DataTransferObjects
{
    /// <summary>
    /// Business error entity
    /// </summary>
    [DataContract]
    public class ValidationErrorDTO : DataTransferObjectBase
    {
        /// <summary>
        /// Property that causes the error.
        /// </summary>
        [DataMember]
        public string PropertyName { get; set; }

        /// <summary>
        /// Error message content
        /// </summary>
        [DataMember]
        public string Message { get; set; }

        /// <summary>
        /// Reserved information
        /// </summary>
        [DataMember]
        public string Tag { get; set; }

        /// <summary>
        /// Instance of business entity.
        /// </summary>
        [DataMember]
        public object Target { get; set; }

        [DataMember]
        public string ErrorCode { get; set; }

        /// <summary>
        /// Default constructor
        /// </summary>
        public ValidationErrorDTO()
        { }

        //public ValidationErrorDTO(Expression<Func<BusinessErrors, object>> expression)
        //    : this(expression, null)
        //{
        //}

        //public ValidationErrorDTO(Expression<Func<BusinessErrors, object>> expression, params object[] args)
        //{
        //    BusinessErrors err = new BusinessErrors();

        //    if (args != null)
        //        Message = string.Format((string)expression.Compile().Invoke(err), args);
        //    else
        //        Message = (string)expression.Compile().Invoke(err);

        //    //ErrorCode = Helper.GetPropertyName(expression);
        //}

        /// <summary>
        /// Constructor with some input parameters
        /// </summary>
        /// <param name="key"></param>
        /// <param name="message"></param>
        /// <param name="tag"></param>
        /// <param name="target"></param>
        public ValidationErrorDTO(string key, string message, string tag, object target, string errorCode)
            : this(key, message, tag, target)
        {
            this.ErrorCode = errorCode;
        }
        /// <summary>
        /// Constructor with some input parameters
        /// </summary>
        /// <param name="key"></param>
        /// <param name="message"></param>
        /// <param name="tag"></param>
        /// <param name="target"></param>
        public ValidationErrorDTO(string key, string message, string tag, object target)
        {
            this.PropertyName = key;
            this.Message = message;
            this.Tag = tag;
            if (target != null)
            {
                this.Target = (target is string) ? target : target.GetType().FullName;
            }
            this.ErrorCode = message;
        }

        /// <summary>
        /// AddressValidation Result if any.
        /// </summary>
        //[DataMember]
        //public AddressValidationErrorDTO AddressValidationResult { get; set; }

    }

    /// <summary>
    /// Collection of business error entities
    /// </summary>
    [DataContract]
    public class ValidationErrorsDTO : DataTransferObjectBase
    {
        public List<ValidationErrorDTO> errorList = new List<ValidationErrorDTO>();

        /// <summary>
        /// List of Validation Error entity.
        /// </summary>
        [DataMember]
        public List<ValidationErrorDTO> ErrorList
        {
            get { return errorList; }
            set { errorList = value; }
        }

        /// <summary>
        /// Constructor with an input parameter.
        /// </summary>
        /// <param name="error"></param>
        public ValidationErrorsDTO(ValidationErrorDTO error)
        {
            errorList.Add(error);
        }

        /// <summary>
        /// Constructor with an input parameter.
        /// </summary>
        /// <param name="error"></param>
        public ValidationErrorsDTO(List<ValidationErrorDTO> errors)
        {
            errorList.AddRange(errors);
        }

        /// <summary>
        /// Default constructor
        /// </summary>
        public ValidationErrorsDTO() { }
    }

    public class ValidationErrorDTOComparer : IEqualityComparer<ValidationErrorDTO>
    {
        #region IEqualityComparer<ValidationErrorDTO> Members

        public bool Equals(ValidationErrorDTO x, ValidationErrorDTO y)
        {
            return (x.ErrorCode == y.ErrorCode
                && x.Message == y.Message
                && x.PropertyName == y.PropertyName
                && x.Tag == y.Tag
                && x.Target == y.Target);
        }

        public int GetHashCode(ValidationErrorDTO obj)
        {
            return 0;
        }

        #endregion
    }

    /// <summary>
    /// Helper classes with some extend methods.
    /// </summary>
    public static class ValidationErrorDTOHelper
    {
        public static List<ValidationErrorDTO> ToValidationErrorDTOList(this ValidationResults validationResults)
        {
            return validationResults.Select(r => r.ToValidationErrorDTO()).ToList();
        }

        public static ValidationErrorsDTO ToValidationErrors(this ValidationResults results)
        {
            if (results == null || results.IsValid)
                return null;

            ValidationErrorsDTO errors = new ValidationErrorsDTO();
            errors.ErrorList.AddRange(results.ToValidationErrorDTOList());

            return errors;
        }

        public static ValidationErrorsDTO ToValidationErrors(this Exception exception)
        {
            if (exception == null)
                return null;

            while (exception.InnerException != null && string.IsNullOrEmpty(exception.Message))
            {
                exception = exception.InnerException;
            }

            ValidationErrorsDTO errors = new ValidationErrorsDTO();
            errors.ErrorList.Add(new ValidationErrorDTO("System Error", exception.Message, exception.StackTrace, exception.GetType().FullName));
            return errors;
        }

        public static void AddValidationErrors(this ValidationErrorsDTO validationErrors, Exception exception)
        {
            ValidationErrorDTO error = new ValidationErrorDTO();
            error.Message = exception.Message;
            validationErrors.ErrorList.Add(error);
        }

        //public static List<ValidationErrorDTO> ConvertValidationResultsToValidationErrors(ValidationResults validationResults)
        //{
        //    return validationResults.ToValidationErrorDTOList();
        //}

        /// <summary>
        /// ValidationResult.Message should contain the error code that 
        /// will be used to populated actual message from database.
        /// 
        /// ValidationErrorDTO.Message should be left empty and will 
        /// be populated from database later.
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        public static ValidationErrorDTO ToValidationErrorDTO(this ValidationResult v)
        {
            return new ValidationErrorDTO()
            {
                ErrorCode = v.Message,
                PropertyName = v.Key,
                Message = string.Empty,
                Tag = v.Tag,
                Target = v.Target != null ? v.Target.GetType().FullName : string.Empty,
            };
        }
    }

    /// <summary>
    /// ValidationException
    /// </summary>
    public class ValidationException : Exception
    {
        /// <summary>
        /// 
        /// </summary>
        public ValidationErrorsDTO Details { get; set; }

        public ValidationException()
            : base()
        {
        }
        public ValidationException(string message)
            : base(message)
        {
            Details = new ValidationErrorsDTO();
            Details.ErrorList.Add(new ValidationErrorDTO(null, message, null, null));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="errors"></param>
        public ValidationException(List<ValidationErrorDTO> errors)
            : base()
        {
            Details = new ValidationErrorsDTO(errors);
        }
    }
}
